﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;

namespace Qreed.Windows.Forms
{
    public class MessageSniffer : NativeWindow
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="MessageSniffer"/> class.
        /// </summary>
        /// <param name="recursive">if set to <c>true</c> child controls are included.</param>
        public MessageSniffer(bool recursive)
        {
            _recursive = recursive;
        }

        private bool _recursive;
        /// <summary>
        /// Gets a value indicating whether this <see cref="MessageSniffer"/> is including child controls.
        /// </summary>
        /// <value><c>true</c> if recursive; otherwise, <c>false</c>.</value>
        public bool Recursive
        {
            get { return this._recursive; }
        }

        private Control _targetControl;
        /// <summary>
        /// Gets or sets the target control.
        /// </summary>
        /// <value>The target control.</value>
        public Control TargetControl
        {
            get { return this._targetControl; }
            set 
            {
                ReleaseControl();
                this._targetControl = value;
                AssignControl();
            }
        }

        private MessageSniffer _parent;
        /// <summary>
        /// Gets the parent sniffer.
        /// </summary>
        /// <value>The parent.</value>
        public MessageSniffer Parent
        {
            get { return this._parent; }
            internal set { this._parent = value; }
        }

        /// <summary>
        /// Gets the root sniffer.
        /// </summary>
        /// <value>The root sniffer.</value>
        public MessageSniffer RootSniffer
        {
            get
            {
                if (Parent == null)
                    return this;
                else
                    return Parent.RootSniffer;
            }
        }

        private bool _pause;
        /// <summary>
        /// Gets or sets a value indicating whether this <see cref="MessageSniffer"/> is paused.
        /// </summary>
        /// <value><c>true</c> if paused; otherwise, <c>false</c>.</value>
        internal bool Pause
        {
            get { return this._pause; }
            set { this._pause = value; }
        }

			        
        private List<MessageSniffer> _childSniffers = new List<MessageSniffer>();

        private void AssignControl()
        {
            if (_targetControl == null)
                return;

            if (Recursive)
            {
                _targetControl.ControlAdded += new ControlEventHandler(OnTargetControlControlAdded);
                _targetControl.ControlRemoved += new ControlEventHandler(OnTargetControlControlRemoved);

                foreach (Control c in TargetControl.Controls)
                {
                    AddChildSniffer(c);
                }
            }

            if (_targetControl.Handle != IntPtr.Zero)
                AssignHandle(_targetControl.Handle);
            
            _targetControl.HandleCreated += new EventHandler(OnTargetControlHandleCreated);
            _targetControl.HandleDestroyed += new EventHandler(OnTargetControlHandleDestroyed);
        }

        private void AddChildSniffer(Control c)
        {
            MessageSniffer sniffer = new MessageSniffer(true);
            sniffer.Parent = this;
            sniffer.TargetControl = c;

            _childSniffers.Add(sniffer);
        }

        void OnTargetControlControlRemoved(object sender, ControlEventArgs e)
        {
            for (int i = 0; i < _childSniffers.Count; i++)
            {
                MessageSniffer child = _childSniffers[i];

                if (child.TargetControl == e.Control)
                {
                    child.ReleaseControl();
                    _childSniffers.RemoveAt(i);
                    break;
                }
            }
        }

        void OnTargetControlControlAdded(object sender, ControlEventArgs e)
        {
            AddChildSniffer(e.Control);
        }

        private void ReleaseControl()
        {
            if (_targetControl == null)
                return;

            if (Recursive)
            {
                foreach (MessageSniffer child in _childSniffers)
                    child.ReleaseControl();

                _childSniffers.Clear();

                _targetControl.ControlAdded -= new ControlEventHandler(OnTargetControlControlAdded);
                _targetControl.ControlRemoved -= new ControlEventHandler(OnTargetControlControlRemoved);
            }

            _targetControl.HandleCreated -= new EventHandler(OnTargetControlHandleCreated);
            _targetControl.HandleDestroyed -= new EventHandler(OnTargetControlHandleDestroyed);

            ReleaseHandle();

            _targetControl = null;
        }			

        void OnTargetControlHandleCreated(object sender, EventArgs e)
        {
            if (Handle != IntPtr.Zero)
                ReleaseHandle();

            AssignHandle(_targetControl.Handle);
        }

        void OnTargetControlHandleDestroyed(object sender, EventArgs e)
        {
            ReleaseControl();
        }

        /// <summary>
        /// Occurs when a message has been received.
        /// </summary>
        public event EventHandler<MessageEventArgs> MessageReceived;

        /// <summary>
        /// Invokes the default window procedure associated with this window.
        /// </summary>
        /// <param name="m">A <see cref="T:System.Windows.Forms.Message"/> that is associated with the current Windows message.</param>
        protected override void WndProc(ref Message m)
        {
            if (!Pause) // To prevent stack overflow
            {
                Pause = true;
                MessageEventArgs e = new MessageEventArgs(ref m, this);

                try
                {
                    RootSniffer.OnWndProc(e); // Dispatch to the root sniffer where the event handling is set
                }
                finally
                {
                    Pause = false;
                }

                if (e.Handled)
                    return;
            }

            base.WndProc(ref m);
        }

        internal void OnWndProc(MessageEventArgs e)
        {
            if (MessageReceived != null)
                MessageReceived(this, e);            
        }

        /// <summary>
        /// Used by the <see cref="MessageReceived"/> event.
        /// </summary>
        public class MessageEventArgs : EventArgs
        {
            /// <summary>
            /// Initializes a new instance of the <see cref="MessageEventArgs"/> class.
            /// </summary>
            /// <param name="message">The message.</param>
            /// <param name="sniffer">The sniffer.</param>
            internal MessageEventArgs(ref Message message, MessageSniffer sniffer)
            {
                _messageSniffer = sniffer;
                _message = message;
            }

            private MessageSniffer _messageSniffer;
            /// <summary>
            /// Gets the message sniffer that received the message.
            /// </summary>
            /// <value>The message sniffer.</value>
            public MessageSniffer MessageSniffer
            {
                get { return this._messageSniffer; }
            }

            /// <summary>
            /// Gets the control that was receiving the message.
            /// </summary>
            /// <value>The control.</value>
            public Control Control
            {
                get {return _messageSniffer.TargetControl; }
            }

            private Message _message;
            /// <summary>
            /// Gets the message.
            /// </summary>
            /// <value>The message.</value>
            public Message Message
            {
                get { return this._message; }
            }

            private bool _handled;
            /// <summary>
            /// Gets or sets a value indicating whether this <see cref="MessageEventArgs"/> is handled.
            /// </summary>
            /// <value><c>true</c> if handled; otherwise, <c>false</c>.</value>
            public bool Handled
            {
                get { return this._handled; }
                set { this._handled = value; }
            }
        }
    }

    
}
